package clang.functions;

import java.util.HashMap;
import java.util.Vector;

import clang.data.Expression;
import clang.data.VarType;
import clang.data.Variable;

public class StdLib {
	private static StdLib instance = null;
	private HashMap<String, Function> sigs = new HashMap<String, Function>();
	
	private static final int STD_DBGPRINT = 0;
	private static final int STD_PRINTF = 1;
	
	private StdLib()
	{
		initFuncs();
	}
	
	private void initFuncs()
	{
		FunctionSignature fsig0 = new FunctionSignature(null, VarType.INT, "dbgprint", true, STD_DBGPRINT);
		sigs.put(fsig0.getName(), new Function(fsig0, STD_DBGPRINT));
		
		
		FunctionSignature fsig1 = new FunctionSignature(null, VarType.INT, "printf", true, STD_PRINTF);
		fsig1.appendParameter(VarType.INT, "stringPtr");
		sigs.put(fsig1.getName(), new Function(fsig1, STD_PRINTF));
	}
	
	public static StdLib get()
	{
		if(instance == null)
			instance = new StdLib();
		return instance;
	}
	
	private static Variable std_dbgprint(Vector<Expression> params)
	{
		//System.out.println("-- DBGPRINT ---");
		for(Expression v : params)
			System.out.println(v);
		//System.out.println("-- DBGPRINT ---");
		
		return new Variable(VarType.INT, 0.0);
	}
	
	private static Variable std_printf(Vector<Expression> params)
	{
		try {
			Variable spoint = Variable.getValueAtVar(params.get(0).calculate().getInteger());
			
			boolean esc = false;
			int argc = 1;
			while(spoint.getData() != 0)
			{
				char c = (char)spoint.getData();
				switch(c)
				{
				case '%':
					if(esc)
					{
						System.out.print("%");
						esc = false;
					}
					else
					{
						esc = true;
					}
					break;
					
				case 'd': case 'i':
					if(esc)
					{
						System.out.print(params.get(argc).calculate().getInteger());
						argc++;
						esc = false;
					}
					else
					{
						System.out.print(c);
					}
					break;
				
				case 'f':
					if(esc)
					{
						System.out.print(params.get(argc).calculate().getData());
						argc++;
						esc = false;
					}
					else
					{
						System.out.print(c);
					}
					break;
					
				default:
					if(esc)
						throw new Exception("Invalid printf formatting!");
					else
						System.out.print(c);
					break;
				}
				
				spoint = Variable.getValueAtVar(spoint.getAddress() + 1);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
			return new Variable(1);
		}
		
		return new Variable(0);
	}
	
	public Function findFunction(FunctionCall call)
	{
		Function f = sigs.get(call.getName());
		if(f == null) return null;
		if(f.getSignature().matches(call))
			return f;
		else
			return null;
	}
	
	public Variable execFunction(int uid, Vector<Expression> params)
	{
		switch(uid)
		{
		case STD_DBGPRINT:
			return std_dbgprint(params);
		case STD_PRINTF:
			return std_printf(params);
		}
		
		return new Variable(VarType.VOID);
	}
}
